﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace SharpCrack
{
	/// <summary>
	/// Base class for hash crackers.
	/// </summary>
	public abstract class HashCracker
	{
		#region Properties
		/// <summary>
		/// Gets or sets hash algorithm.
		/// </summary>
		public HashAlgorithm Algorithm { get; set; }

		/// <summary>
		/// Gets or sets how many times hash guesses before matching.
		/// </summary>
		public int HashFunctionExponent { get; set; }

		/// <summary>
		/// Gets the collection of hashes to crack.
		/// </summary>
		public IList<byte[]> Hashes { get; protected set; }

		/// <summary>
		/// 
		/// </summary>
		public bool CancellationPending { get; set; }
		
		/// <summary>
		/// Gets a list of transforms that are applied for each word of the wordlist.
		/// </summary>
		public IList<WordTransform> WordTransforms { get; protected set; }
		
		/// <summary>
		/// Gets the index of the current word of the wordlist.
		/// </summary>
		public int WordlistPosition { get; protected set; }
		#endregion

		protected int allGuesses = 0;

		#region Events
		/// <summary>
		/// Occurrs when a collision has found.
		/// </summary>
		public event EventHandler<CollisionFoundEventArgs> CollisionFound;

		/// <summary>
		/// Occurrs when cracking has started.
		/// </summary>
		public event EventHandler<AttackStartedEventArgs> AttackStarted;

		/// <summary>
		/// Occurrs when cracking has been finished.
		/// </summary>
		public event EventHandler<AttackFinishedEventArgs> AttackFinished;
		#endregion

		#region Methods
		/// <summary>
		/// Initializes a new attack session.
		/// </summary>
		protected virtual void InitializeAttack()
		{
			this.allGuesses = 0;
			this.CancellationPending = false;
		}

		/// <summary>
		/// Starts a brute force attack.
		/// </summary>
		/// <param name="charset"></param>
		/// <param name="maxLength"></param>
		public virtual void StartBruteForceAttack(string charset, int maxLength)
		{
			this.StartBruteForceAttack(charset, 0, maxLength);
		}
		/// <summary>
		/// Starts a brute force attack.
		/// </summary>
		/// <param name="charset">The character set.</param>
		/// <param name="minLength">The minimum length of permutations will be generated.</param>
		/// <param name="maxLength">The maximum length of permutations will be generated.</param>
		public virtual void StartBruteForceAttack(string charset, int minLength, int maxLength)
		{
			this.StartBruteForceAttack(charset, minLength, maxLength, new String(charset[0], minLength));
		}
		/// <summary>
		/// Starts a brute force attack.
		/// </summary>
		/// <param name="charset">The character set.</param>
		/// <param name="minLength">The minimum length of permutations will be generated.</param>
		/// <param name="maxLength">The maximum length of permutations will be generated.</param>
		/// <param name="startingPermutation">The starting permutation.</param>
		public abstract void StartBruteForceAttack(string charset, int minLength, int maxLength, string startingPermutation);

		/// <summary>
		/// Starts a dictionary attack.
		/// </summary>
		/// <param name="wordlistPath">Path of the file that contains a list of words.</param>
		/// <param name="initialPosition">First line index that will be processed.</param>
		/// <remarks>Words in the wordlist are stored in lines. Each line represents a word.</remarks>
		public abstract void StartDictionaryAttack(string wordlistPath, int initialPosition = 0);
		
		/// <summary>
		/// Computes the hash of a candidate and compares to the hashes in the queue.
		/// </summary>
		/// <param name="str"></param>
		public abstract void TryString(string candidate);

		/// <summary>
		/// Processes a candidate with the specified transforms.
		/// </summary>
		/// <param name="candidate"></param>
		protected virtual void ProcessCandidate(string candidate)
		{
			foreach (WordTransform t in this.WordTransforms)
				this.TryString(t.Transform(candidate));
		}

		/// <summary>
		/// Stops current attack.
		/// </summary>
		public virtual void Stop()
		{
			this.CancellationPending = true;
		}
		#endregion

		#region Inner event handlers
		protected virtual void OnCollisionFound(CollisionFoundEventArgs e)
		{
			if (CollisionFound != null)
				CollisionFound(this, e);
		}

		protected virtual void OnAttackStarted(AttackStartedEventArgs e)
		{
			if (AttackStarted != null)
				AttackStarted(this, e);
		}

		protected virtual void OnAttackFinished(AttackFinishedEventArgs e)
		{
			if (AttackFinished != null)
				AttackFinished(this, e);
		}
		#endregion
	}
}
